package cn.com.surker.system.controller;

import cn.com.surker.common.AbstractController;
import cn.com.surker.common.Code;
import cn.com.surker.common.Constant;
import cn.com.surker.enums.StateEnum;
import cn.com.surker.oa.entity.OaEnclosure;
import cn.com.surker.spring.RestResultDto;
import cn.com.surker.system.model.CacheUser;
import cn.com.surker.system.service.OaEnclosureService;
import cn.com.surker.util.DateUtil;
import cn.com.surker.util.FileUtils;
import cn.com.surker.util.StringUtils;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 *
 * 文件上传类
 * @author luotao
 * @date 2022/11/3
 */
@Controller
@RequestMapping("/file")
public class FileController extends AbstractController {

	private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";

	@Autowired
	private OaEnclosureService oaEnclosureService;

	/**
	 * 修改业务实例文件上传具体实现方法;
	 *
	 * @param file
	 * @return
	 */
	@RequestMapping(value = "/update/upload",method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto handleFileUpload(@RequestParam(name = "file")MultipartFile file, String bizId,
										  String bizType, HttpServletRequest request) {
		CacheUser user = getUser();
		if(user == null){
			return RestResultDto.fail(Code.UN_USER_INFO);
		}
		if(file == null){
			return RestResultDto.fail("上传文件为空");
		}
		// 有文件
		// 文件路径
		String basePath = Constant.OA_BASE_PATH;
		// 拼接日期
		String dateStr = DateUtil.convertString(new Date(), "yyyy-MM-dd");
		String baseOutPath = basePath +  dateStr;
		// 文件路径列表
		List<OaEnclosure> enclosureList = new ArrayList<>();
		Date nowDate = new Date();
		OaEnclosure oaEnclosure = null;
		try {
			Long bid = null;
			if (!StringUtils.isNull(bizId))
				bid = StringUtils.convertLong(bizId);
			oaEnclosure = update(file,baseOutPath,user.getId(),user.getName(),nowDate, bid, bizType);
		} catch (Exception e) {
			e.printStackTrace();
			return RestResultDto.fail(e.getMessage());
		}
		if (oaEnclosure != null){
			boolean save = oaEnclosureService.save(oaEnclosure);
			if(save == true){
				return RestResultDto.success("上传成功",oaEnclosure.getId());
			}else {
				return RestResultDto.success("文件上传成功，但保存路径失败");
			}
		}else {
			return RestResultDto.fail("保存文件失败");
		}
	}

	/**
	 * 文件上传具体实现方法;
	 *
	 * @param file
	 * @return
	 */
	@RequestMapping(value = "/upload",method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto handleFileUpload(@RequestParam(name = "file")MultipartFile file, Long bizId, String bizType, HttpServletRequest request) {
		CacheUser user = getUser();
		if(user == null){
			return RestResultDto.fail(Code.UN_USER_INFO);
		}
		if(file == null){
			return RestResultDto.fail("上传文件为空");
		}
		// 有文件
		// 文件路径
		String basePath = Constant.OA_BASE_PATH;
		// 拼接日期
		String dateStr = DateUtil.convertString(new Date(), "yyyy-MM-dd");
		String baseOutPath = basePath +  dateStr;
		// 文件路径列表
		List<OaEnclosure> enclosureList = new ArrayList<>();
		Date nowDate = new Date();
		OaEnclosure oaEnclosure = null;
		try {
			if (StringUtils.isNull(bizId))
				bizId = null;
			if (StringUtils.isNull(bizType))
				bizType = "";
			oaEnclosure = update(file,baseOutPath,user.getId(),user.getName(),nowDate, bizId, bizType);
		} catch (Exception e) {
			e.printStackTrace();
			return RestResultDto.fail(e.getMessage());
		}
		if (oaEnclosure != null){
			boolean save = oaEnclosureService.save(oaEnclosure);
			if(save == true){
				return RestResultDto.success("上传成功",oaEnclosure.getId());
			}else {
				return RestResultDto.success("文件上传成功，但保存路径失败");
			}
		}else {
			return RestResultDto.fail("保存文件失败");
		}
	}

	/**
	 * 文件上传具体实现方法;
	 *
	 * @param files
	 * @return
	 */
	@RequestMapping(value = "/mult/upload",method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto handleFileMultUpload(@RequestParam(name = "file")MultipartFile[] files, HttpServletRequest request) {
		CacheUser user = getUser();
		if(user == null){
			return RestResultDto.fail(Code.UN_USER_INFO);
		}
		if (files != null && files.length > 0) {
			// 有文件
			// 文件路径
			String basePath = Constant.OA_BASE_PATH;
			// 拼接日期
			String dateStr = DateUtil.convertString(new Date(), "yyyy-MM-dd");
			String baseOutPath = basePath +  dateStr;
			// 文件路径列表
			List<OaEnclosure> enclosureList = new ArrayList<>();
			Date nowDate = new Date();
			for(MultipartFile file : files){
				String resPath = "";
				BufferedOutputStream out = null;
				try {
					OaEnclosure update = update(file, baseOutPath, user.getId(), user.getName(), nowDate, null, "");
					enclosureList.add(update);
				} catch (Exception e) {
					e.printStackTrace();
					return RestResultDto.fail("上传文件失败" + e.getMessage());
				}
			}
			boolean b = oaEnclosureService.saveBatch(enclosureList);
			if(b){
				// 获取id列表
				List<Long> idList = enclosureList.stream().map(OaEnclosure::getId).collect(Collectors.toList());
				// 转换喂json列表
				//String ids = JSONObject.toJSONString(idList);
				return RestResultDto.success("上传成功",idList);
			}else {
				return RestResultDto.fail("上传失败:数据保存失败！");
			}
		} else {
			return RestResultDto.fail("上传失败，因为文件是空的.");
		}
	}

	/**
	 * 获取图片预览图
	 */
	@RequestMapping("/preview/{id}")
	public void preview(@PathVariable("id") Long enclosureId,HttpServletResponse response){
		if(enclosureId == null){
			return;
		}
		OaEnclosure oaEnclosure = oaEnclosureService.selectById(enclosureId);
		if(oaEnclosure != null){
			String fileSuffix = oaEnclosure.getFileSuffix();
			Boolean isImage = FileUtils.isImage(fileSuffix);
			if(!isImage){
				return;
			}
			String fileName = oaEnclosure.getFileName();
			String filePath = oaEnclosure.getFilePreview();
			response.setHeader("content-type","application/octet-stream");
			response.setContentType("application/octet-stream");
			try {
				response.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(fileName,"UTF-8"));
			}catch (UnsupportedEncodingException e2) {
				e2.printStackTrace();
			}
			byte[] buff =new byte[1024];
			BufferedInputStream bis =null;
			OutputStream os =null;
			try {
				File file =new File(filePath);
				if(!file.exists()){
					// 文件被删除了
					return;
				}
				os = response.getOutputStream();
				bis =new BufferedInputStream(new FileInputStream(file));
				int i = bis.read(buff);
				while (i != -1) {
					os.write(buff,0, buff.length);
					os.flush();
					i = bis.read(buff);
				}
			}catch (IOException e1) {
				e1.printStackTrace();;
			}catch (Exception e) {
				e.printStackTrace();
			}finally {
				if (bis !=null) {
					try {
						bis.close();
					}catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	/**
	 * 下载文件
	 * @param enclosureId
	 * @param response
	 * @throws Exception
	 */
	@RequestMapping("/down/{id}")
	public void down(@PathVariable("id") Long enclosureId, HttpServletResponse response) throws Exception {
		if(enclosureId == null){
			return;
		}
		OaEnclosure oaEnclosure = oaEnclosureService.selectById(enclosureId);
		if(oaEnclosure != null){
			String fileName = oaEnclosure.getFileName();
			String filePath = oaEnclosure.getFilePath();
			String contentType = MediaTypeFactory.getMediaType(fileName).orElse(MediaType.APPLICATION_OCTET_STREAM).toString();
			String encodeFileName = URLEncoder.encode(fileName, "UTF-8");
			response.setHeader("content-type","application/octet-stream");
			response.setHeader("Content-Disposition","attachment;filename=" + encodeFileName);
			response.addHeader("Content-Length",oaEnclosure.getFileSize());
			response.setContentType(contentType);
			byte[] buff =new byte[1024];
			BufferedInputStream bis =null;
			OutputStream os =null;
			try {
				File file =new File(filePath);
				if(!file.exists()){
					// 文件被删除了
					return;
				}
				os = response.getOutputStream();
				bis =new BufferedInputStream(new FileInputStream(file));
				int i = bis.read(buff);
				while (i != -1) {
					os.write(buff,0, buff.length);
					i = bis.read(buff);
				}
				os.flush();
			}catch (IOException e1) {
				e1.printStackTrace();;
			}catch (Exception e) {
				e.printStackTrace();
			}finally {
				if (bis !=null) {
					try {
						bis.close();
					}catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	/**
	 * 根据biz查询附件列表
	 * @param bizTable		业务表
	 * @param bizId			业务id
	 * @return
	 */
	@RequestMapping(value = "/list",method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto list(String bizTable,Long bizId){
		CacheUser user = getUser();
		if(user == null){
			return RestResultDto.fail(Code.UN_USER_INFO);
		}
		if(bizId == null || StringUtils.isBlank(bizTable)){
			return RestResultDto.fail(Code.PARAM_EMPTY);
		}
		// 查询附件列表
		OaEnclosure oaEnclosure = new OaEnclosure();
		// 暂不做根据用户id筛选
		oaEnclosure.setTableName(bizTable);
		oaEnclosure.setBizId(bizId);
		oaEnclosure.setStatus(StateEnum.ENABLED.getCode());
		List<OaEnclosure> all = oaEnclosureService.findAll(oaEnclosure);
		return RestResultDto.success(all);
	}

	/**
	 * 删除附件（逻辑删除数据，真实删除文件）
	 * @param id			附件id
	 * @return
	 */
	@RequestMapping(value = "/delete/{id}",method = RequestMethod.DELETE)
	@ResponseBody
	public RestResultDto delete(@PathVariable Long id){
		CacheUser user = getUser();
		if(user == null){
			return RestResultDto.fail(Code.UN_USER_INFO);
		}
		if(id == null){
			return RestResultDto.fail(Code.PARAM_EMPTY);
		}
		// 查询数据
		OaEnclosure oaEnclosure = oaEnclosureService.getById(id);
		if(oaEnclosure == null){
			return RestResultDto.fail(Code.NO_DATA);
		}
		// 判断是否同一个用户
		Long loginUserId = user.getId();
		Long enclosureUid = oaEnclosure.getUid();
		if(!loginUserId.equals(enclosureUid)){
			return RestResultDto.fail("该文件不是您上传的，不允许操作");
		}
		// 删除文件
		// 判断是否图片，如果是图片，则还要删除预览图
		String fileName = oaEnclosure.getFileName();
		String fileSuffix = FileUtils.getFileSuffix(fileName);
		if(FileUtils.isImage(fileSuffix)){
			// 是图片，删除预览图
			String previewPath = oaEnclosure.getFilePreview();
			FileUtils.deleteFile(previewPath);
		}
		// 删除附件数据
		String filePath = oaEnclosure.getFilePath();
		FileUtils.deleteFile(filePath);
		// 逻辑删除数据
		oaEnclosureService.delete(id);
		return RestResultDto.success("删除成功");
	}

	/**
	 * 根据图片路径下载文件
	 */
	@RequestMapping("/path")
	public void downByPath(String filePath,HttpServletResponse response){
		if(filePath == null){
			return;
		}
		String[] split = filePath.split("/");
		String fileName = split[split.length - 1];
		response.setHeader("content-type","application/octet-stream");
		response.setContentType("application/octet-stream");
		try {
			response.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(fileName,"UTF-8"));
		}catch (UnsupportedEncodingException e2) {
			e2.printStackTrace();
		}
		byte[] buff =new byte[1024];
		BufferedInputStream bis =null;
		OutputStream os =null;
		try {
			File file =new File(filePath);
			if(!file.exists()){
				// 文件被删除了
				return;
			}
			os = response.getOutputStream();
			bis =new BufferedInputStream(new FileInputStream(file));
			int i = bis.read(buff);
			while (i != -1) {
				os.write(buff,0, buff.length);
				os.flush();
				i = bis.read(buff);
			}
		}catch (IOException e1) {
			e1.printStackTrace();;
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			if (bis !=null) {
				try {
					bis.close();
				}catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 保存图片预览图
	 * @param file					图片文件
	 * @param suffix				图片后缀
	 * @param baseOutPath			路径
	 * @param fileId				文件id
	 * @return
	 */
	private String savePreviewImg(File file, String suffix, String baseOutPath, String fileId) {
		if(file == null){
			return "";
		}
		Image srcFile = null;
		String newImage = null;
		FileOutputStream out = null;
		try {
			// 文件不存在
			if (!file.exists()) {
				return null;
			}
			/*读取图片信息*/
			srcFile = ImageIO.read(file);
			int width = Constant.IMG_PREVIEW_WIDTH;
			int height = Constant.IMG_PREVIEW_HEIGHT;
			boolean percentage = true;
			if (percentage) {
				// 为等比缩放计算输出的图片宽度及高度
				double rate1 = ((double) srcFile.getWidth(null)) / (double) width;
				double rate2 = ((double) srcFile.getHeight(null)) / (double) height;
				double rate = rate1 > rate2 ? rate1 : rate2;
				width = (int) (((double) srcFile.getWidth(null)) / rate);
				height = (int) (((double) srcFile.getHeight(null)) / rate);
			}
			/* 宽高设定*/
			BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			tag.getGraphics().drawImage(srcFile, 0, 0, width, height, null);
			newImage = baseOutPath + "/" + fileId + "." + Constant.IMG_PREVIEW_SMALLICON + "." + suffix;
			/*压缩之后临时存放位置*/
			out = new FileOutputStream(newImage);
			JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
			JPEGEncodeParam jep = JPEGCodec.getDefaultJPEGEncodeParam(tag);
			/* 压缩质量 */
			jep.setQuality(Constant.IMG_PREVIEW_QUALITY, true);
			encoder.encode(tag, jep);
			ImageIO.write(tag,suffix,out);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			srcFile.flush();
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return newImage;
	}

	/**
	 * 压缩图片方法(保留做参考用）
	 * @param oldFile    将要压缩的图片
	 * @param width      压缩宽
	 * @param height     压缩高
	 * @param quality    压缩清晰度 <b>建议为1.0</b>
	 * @param smallIcon  压缩图片后,添加的扩展名（在图片后缀名前添加）
	 * @param percentage 是否等比压缩 若true宽高比率将将自动调整
	 * @return 如果处理正确返回压缩后的文件名 null则参数可能有误
	 */
	private String doCompress(String oldFile, int width, int height, float quality, String smallIcon, boolean percentage) {
		if (oldFile != null && width > 0 && height > 0) {
			Image srcFile = null;
			String newImage = null;
			FileOutputStream out = null;
			try {
				File file = new File(oldFile);
				// 文件不存在
				if (!file.exists()) {
					return null;
				}
				/*读取图片信息*/
				srcFile = ImageIO.read(file);
				int new_w = width;
				int new_h = height;
				if (percentage) {
					// 为等比缩放计算输出的图片宽度及高度
					double rate1 = ((double) srcFile.getWidth(null)) / (double) width;
					double rate2 = ((double) srcFile.getHeight(null)) / (double) height;
					double rate = rate1 > rate2 ? rate1 : rate2;
					new_w = (int) (((double) srcFile.getWidth(null)) / rate);
					new_h = (int) (((double) srcFile.getHeight(null)) / rate);
				}
				/* 宽高设定*/
				BufferedImage tag = new BufferedImage(new_w, new_h, BufferedImage.TYPE_INT_RGB);
				tag.getGraphics().drawImage(srcFile, 0, 0, new_w, new_h, null);

				/*压缩后的文件名 */
				String filePrex = oldFile.substring(0, oldFile.lastIndexOf('.'));
				newImage = filePrex + smallIcon + oldFile.substring(filePrex.length());

				/*压缩之后临时存放位置*/
				out = new FileOutputStream(newImage);

				JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
				JPEGEncodeParam jep = JPEGCodec.getDefaultJPEGEncodeParam(tag);

				/* 压缩质量 */
				jep.setQuality(quality, true);
				encoder.encode(tag, jep);


			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				srcFile.flush();
				if (out != null) {
					try {
						out.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
			return newImage;
		} else {
			return null;
		}
	}


	@RequestMapping(value = "/preview", method = RequestMethod.GET)
	public void preview(HttpServletRequest request, HttpServletResponse response, String img){
		if (StringUtils.isNull(img))
			return;
		//禁止缓存
		response.setHeader("Pragma", "No-cache");
		response.setHeader("Cache-Control", "No-cache");
		response.setDateHeader("Expires", 0);
		// 指定生成的响应是图片
		response.setContentType("image/jpeg");
		File file = null;
		BufferedImage image = null;
		try{
			file = new File(img);
			if (file == null)
				return;

			image = ImageIO.read(file);
			/**写入图片的格式**/
			ImageIO.write(image, "jpeg", response.getOutputStream());
			/**根据图片项目的物理路径生成输出流**/
			OutputStream outImage = new FileOutputStream(img);
			JPEGImageEncoder enc = JPEGCodec.createJPEGEncoder(outImage);
			enc.encode(image);
			outImage.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}


	/**
	 * 存储文件实际接口
	 * @param file
	 * @param baseOutPath
	 * @param uid
	 * @param userName
	 * @param nowDate
	 * @param bizId
	 * @param bizType
	 * @return
	 * @throws Exception
	 */
	private OaEnclosure update(MultipartFile file,String baseOutPath,Long uid,String userName,
							   Date nowDate, Long bizId, String bizType) throws Exception {
		BufferedOutputStream out = null;
		OaEnclosure resEnclosure = null;
		try {
			// 验证文件大小
			Long size = file.getSize();
			// 文件名称
			String originalFilename = file.getOriginalFilename();
			// 获取文件格式
			String[] split = originalFilename.split("\\.");
			String fileSuffix = split[split.length - 1];
			// 文件ID
			String fileId = UUID.randomUUID().toString();
			// 文件名
			fileId = fileId + "." + fileSuffix;
			// 最终路径
			String resPath = baseOutPath +  "/" + fileId;
			// 文件输出目录
			File outFile = new File(resPath);
			if(!outFile.exists()){
				File parentFile = outFile.getParentFile();
				if(!parentFile.exists()){
					parentFile.mkdirs();
				}
				// 创建文件
				outFile.createNewFile();
			}
			out = new BufferedOutputStream(new FileOutputStream(outFile));
			out.write(file.getBytes());
			out.flush();
			// 判断是否是图片类型，如果是图片类型，存预览图
			String filePreview = "";
			Boolean isImage = FileUtils.isImage(fileSuffix);
			if(isImage){
				// 是图片类型，存储预览图
				filePreview = savePreviewImg(outFile,fileSuffix,baseOutPath,fileId);
			}
			// 文件创建成功,存储数据
			OaEnclosure oaEnclosure = new OaEnclosure();
			oaEnclosure.setFileId(fileId);
			oaEnclosure.setFileName(originalFilename);
			oaEnclosure.setFileSuffix(fileSuffix);
			oaEnclosure.setFilePath(resPath);
			oaEnclosure.setFilePreview(filePreview);
			oaEnclosure.setFileSize(size.toString());
			oaEnclosure.setPageView(0);
			oaEnclosure.setPageDown(0);
			if (!StringUtils.isNull(bizId))
				oaEnclosure.setBizId(bizId);
			if (!StringUtils.isNull(bizType))
				oaEnclosure.setTableName(bizType);
			oaEnclosure.setUid(uid);
			oaEnclosure.setName(userName);
			oaEnclosure.setStatus(StateEnum.ENABLED.getCode());
			oaEnclosure.setCreateTime(nowDate);
			resEnclosure = oaEnclosure;
		} catch (Exception e) {
			throw e;
		} finally {
			if(out != null) {
				try {
					out.close();
				}catch (Exception e){
					throw e;
				}
			}
		}
		return resEnclosure;
	}
}
